<?php 
/**
 * ... ...
 * ... ...
 *
 */ 


/**
 * a category model ,implements by db 
 *
 * @access public
 */
class JCAT_DBAdjacensyCategory
	extends JCAT_DBModel
{
	const BORDER_LEFT = 'nLft' ;
	const BORDER_RIGHT = 'nRgt' ;
	
	/**
	 * 位置内
	 * 
	 * @access	const
	 * @var		int
	 */
	const LOCAL_IN = 1 ;
	/**
	 * 位置外
	 * 
	 * @access	const
	 * @var		int
	 */
	const LOCAL_OUT = 2 ;
	/**
	 * 位置前
	 * 
	 * @access	const
	 * @var		int
	 */
	const LOCAL_FRONT = 4 ;
	/**
	 * 位置后
	 * 
	 * @access	const
	 * @var		int
	 */
	const LOCAL_BEHIND = 8 ;
	/**
	 * 位置前后
	 * 
	 * @access	const
	 * @var		int
	 */
	const LOCAL_BROTHER = 12 ;	
	/**
	 * 同一位置
	 * 
	 * @access	const
	 * @var		int
	 */
	const LOCAL_SAME = 16 ;
	/**
	 * 无效的位置
	 * 
	 * @access	const
	 * @var		int
	 */
	const LOCAL_INVALID = 0 ;
	
	/**
	 * 构造函数 
	 *
	 * @access	public 
	 * @return	void
	 */
	public function JCAT_DBAdjacensyCategory(JCAT_ORM $aORM)
	{
		if( $aORM->GetFieldByProperty(self::BORDER_LEFT)===null 
				or $aORM->GetFieldByProperty(self::BORDER_RIGHT)===null )
		{
		 	throw new JCAT_Exception(JCAT_Language::SentenceEx(
		 		'传递给 邻接分类模型(JCAT_DBAdjacensyCategory)的 ORM对象缺少必须的属性/字段映射（%s,%s）'
		 		, 'JCAT', null ,self::BORDER_LEFT, self::BORDER_RIGHT
		 	)) ;
		}
		
		$this->JCAT_DBModel( $aORM ) ;
	}

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$aModel		JCAT_DBAdjacensyCategory
	 * @return	void
	 */
	public function AddModel( JCAT_IModel $aModel, $sName=null )
	{
		JCAT_ASSERT::ASSERT_INSTANCE($aModel,'JCAT_DBAdjacensyCategory'
			, JCAT_Language::SentenceEx('参数 $aModel 必须为一个JCAT_DBAdjacensyCategory实例','JCAT',null));

		return parent::AddModel($aModel,$aModel->GetLeftBorder()) ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @param	$aAdjacensyCategory		JCAT_DBAdjacensyCategory
	 * @return	void
	 */
	public function AddCategory(JCAT_DBAdjacensyCategory $aInCategory)
	{
		// 设置深度
		$aInCategory->SetTreeDepth($this->GetTreeDepth()+1) ;
		
		$bDirectlyUnder = true ;
		$aIterator = $this->CreateModelIterator() ;
		for ( $aIterator->First(); !$aIterator->IsDone(); $aIterator->Next() )
		{
			$aChildCategory = $aIterator->Current() ;
			
			$nLocal = $aInCategory->Compare($aChildCategory) ;
			
			// 非所属
			if($nLocal&self::LOCAL_BROTHER)
			{
				continue ;
			}
			
			else if($nLocal==self::LOCAL_IN)
			{
				$aChildCategory->AddCategory($aInCategory) ;
				$bDirectlyUnder = false ;
				break ;
			}
			
			else if($nLocal==self::LOCAL_OUT)
			{
				$aInCategory->AddCategory($aChildCategory) ;
			}
			
			else 
			{
				throw new JCAT_Exception(JCAT_Language::SentenceEx(
					'遇到无效的邻接分类层级：(%d,%d) to (%d,%d) ?'
					, 'JCAT', null
					, $aInCategory->GetLeftBorder(), $aInCategory->GetRightBorder()
					, $aChildCategory->GetLeftBorder(), $aChildCategory->GetRightBorder()
				)) ;
			}
		}
		
		// 直属关系
		if($bDirectlyUnder)
		{
			$this->AddModel($aInCategory) ;
		}
		
		// 子分类排序
		$this->aModelContainer->SortByName() ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @param	$nTreeDepth		int
	 * @return	void
	 */
	public function SetTreeDepth($nTreeDepth)
	{
		$this->nTreeDepth = $nTreeDepth ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @return	int
	 */
	public function GetTreeDepth()
	{
		return $this->nTreeDepth ;
	}
	
	/**
	 * get child categories count
	 *
	 * @access	public
	 * @param	$bRecursion=true	bool
	 * @return	int
	 */
	public function GetChildrenCount($bRecursion=true)
	{
		if($bRecursion)
		{
			$nCount = 0 ;
			// 保存下级分类
			$aIter = $this->CreateModelIterator() ;
			for ( $aIter->First(); !$aIter->IsDone(); $aIter->Next() )
			{
				$nCount ++ ;
				
				$aChildItem = $aIter->Current() ;
				$nCount+= $aChildItem->GetChildrenCount(true) ;
			}
			
			return $nCount ;
		}
		else
		{
			return $this->GetModelCount() ;
		}
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @param	$Category		JCAT_DBAdjacensyCategory,int,null
	 * @return	void
	 */
	public function AppendTo($Category=null)
	{
		JCAT_ASSERT::ASSERT_ISTHESE($Category,array('JCAT_DBAdjacensyCategory','int'));
		
		$aORM = $this->GetORM() ;
		$aDB = $aORM->GetDB() ;
	
		if( $Category instanceof JCAT_DBAdjacensyCategory )
		{
			if( !$Category->HaveSerialized() )
			{
				throw new JCAT_Exception(JCAT_Language::SentenceEx(
					'无法将一个 邻接分类Model 做为下级分类，插入到另一个尚未保存的 邻接分类Model之下。'
					, 'JCAT', null
				)) ;
			}
		
			return $this->InsertTo($Category->GetRightBorder()) ;
		}
		
		else if( is_int($Category) )
		{
			$sPrimaryKey = $aORM->GetPrimaryKey() ;
			
			$nParentRgt = $aDB->SelectOneField(
					$aORM->GetHubTable()
					, $aORM->GetFieldByProperty(self::BORDER_RIGHT)
					, "`{$sPrimaryKey}`={$Category}"
			) ;
					
			if( 0>=$nParentRgt )
			{
				throw new JCAT_Exception(JCAT_Language::SentenceEx(
					'无法找到指定的父级分类（primary key:%s）'
					, 'JCAT', null
					, $Category
				)) ;
			}
			
			return $this->InsertTo($nParentRgt) ;
		}
		
		else 
		{
			throw new JCAT_Exception(JCAT_Language::SentenceEx(
				'参数 $Category 必须为以下类型：JCAT_DBAdjacensyCategory,int,null'
				, 'JCAT', null
			)) ;
		}
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @param	$nPos		int
	 * @return	void
	 */
	public function InsertTo($nPos)
	{
		JCAT_ASSERT::ASSERT_($nPos>0,JCAT_Language::SentenceEx('参数 $nPos(%s) 必须大于0','JCAT',null,$nPos)) ;
		
		// 设置当前分类模型的左右边界
		$nAdjacWidth = $this->SetBorder($nPos) ;
		
		// 腾出空间
		// ------------------------
		if( !$this->Vacate($nPos,$nAdjacWidth) )
		{
			throw new JCAT_Exception(JCAT_Language::SentenceEx(
				'移动邻接分类边界失败'
				, 'JCAT', null
			)) ;
		}
		
		// 保存此model
		// ------------------------
		return $this->Create() ;
	}
	
	/**
	 * 腾出空间
	 *
	 * @access	protected
	 * @param	$nPos		int
	 * @param	$nWidth		int
	 * @return	void
	 */
	protected function Vacate($nPos,$nWidth)
	{
		$aORM = $this->GetORM() ;
		$aDB = $aORM->GetDB() ;
		$sKeyBorderRgt = $aORM->GetFieldByProperty(self::BORDER_RIGHT) ;
		$sKeyBorderLft = $aORM->GetFieldByProperty(self::BORDER_LEFT) ;
		$sHubTable = $aORM->GetHubTable() ;
		
		// 移动 右边界
		if( !$aDB->Update($sHubTable,array($sKeyBorderRgt=>JCAT_DB::e("`{$sKeyBorderRgt}`+{$nWidth}")),"`{$sKeyBorderRgt}`>={$nPos}") )
		{
			return false ;
		}
		
		// 向移动 左边界
		if( !$aDB->Update($sHubTable,array($sKeyBorderLft=>JCAT_DB::e("`{$sKeyBorderLft}`+{$nWidth}")),"`{$sKeyBorderLft}`>{$nPos}") )
		{
			return false ;
		}
		
		return true ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @param	$nPos		int
	 * @return	void
	 */
	public function MoveTo($nPos)
	{
		if( !$this->HaveSerialized() or $this->GetRightBorder()<1 or $this->GetLeftBorder()<1 )
		{
			throw new JCAT_Exception(JCAT_Language::SentenceEx(
				'邻接分类Model尚未序列化，无法执行此操作。'
				, 'JCAT', null
			)) ;
		}
		
		$aORM = $this->GetORM() ;
		$aDB = $aORM->GetDB() ;
		$sKeyBorderRgt = $aORM->GetFieldByProperty(self::BORDER_RIGHT) ;
		$sKeyBorderLft = $aORM->GetFieldByProperty(self::BORDER_LEFT) ;
		$sHubTable = $aORM->GetHubTable() ;
		$nOldLft = $this->GetLeftBorder() ;
		$nOldRgt = $this->GetRightBorder() ;
		
		// 暂放到 1 以前
		if( !$aDB->Update($sHubTable,
			array(
				$sKeyBorderLft=>JCAT_DB::e("`{$sKeyBorderLft}`-{$nOldRgt}")
				, $sKeyBorderRgt=>JCAT_DB::e("`{$sKeyBorderRgt}`-{$nOldRgt}") )
			, "`{$sKeyBorderLft}` BETWEEN {$nOldLft} AND {$nOldRgt}") )
		{
			throw new JCAT_Exception(JCAT_Language::SentenceEx(
				'移动邻接分类边界失败'
				, 'JCAT', null
			)) ;
		}
		
		
		// 腾出空间
		$nWidth = $nOldRgt-$nOldLft + 1 ;
		if( $this->Vacate($nPos,$nWidth) )
		{
			throw new JCAT_Exception(JCAT_Language::SentenceEx(
				'移动邻接分类边界失败'
				, 'JCAT', null
			)) ;
		}
		
		// 移动至目标
		$nMove = $nWidth + $nPos - 1 ;
		if( !$aDB->Update($sHubTable,
			array(
				$sKeyBorderLft=>JCAT_DB::e("`{$sKeyBorderLft}`+{$nMove}")
				, $sKeyBorderRgt=>JCAT_DB::e("`{$sKeyBorderRgt}`+{$nMove}") )
			, "`{$sKeyBorderLft}`<1") )
		{
			throw new JCAT_Exception(JCAT_Language::SentenceEx(
				'移动邻接分类边界失败'
				, 'JCAT', null
			)) ;
		}
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @param	$aWith	JCAT_DBAdjacensyCategory
	 * @return	void
	 */
	public function Compare(JCAT_DBAdjacensyCategory $aCategory)
	{
		if( $this->GetRightBorder() < $aCategory->GetLeftBorder() )
		{
			return self::LOCAL_FRONT ;
		}
		
		else if( $this->GetLeftBorder() > $aCategory->GetRightBorder() )
		{
			return self::LOCAL_BEHIND ;
		}
		
		else if( $this->GetLeftBorder() < $aCategory->GetLeftBorder()
				and $this->GetRightBorder() > $aCategory->GetRightBorder() )
		{
			return self::LOCAL_OUT ;
		}
		
		else if( $this->GetLeftBorder() > $aCategory->GetLeftBorder()
				and $this->GetRightBorder() < $aCategory->GetRightBorder() )
		{
			return self::LOCAL_IN ;
		}
		
		else if( $this->GetLeftBorder() == $aCategory->GetLeftBorder()
				and $this->GetRightBorder() == $aCategory->GetRightBorder() )
		{
			return self::LOCAL_SAME ;
		}
		
		else
		{
			return self::LOCAL_INVALID ;
		}
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @return	bool
	 */
	public function Delete()
	{
		if( !$this->HaveSerialized() )
		{
			return true ;
		}
		
		$aORM = $this->GetORM() ;
		$aDB = $aORM->GetDB() ;
		$nLft = $this->GetLeftBorder() ;
		$nRgt = $this->GetRightBorder() ;
		$sKeyBorderLft = $aORM->GetFieldByProperty(self::BORDER_LEFT) ;
		$sKeyBorderRgt = $aORM->GetFieldByProperty(self::BORDER_RIGHT) ;
		$sHubTable = $aORM->GetHubTable() ;

		// 删除分类
		if( !parent::Delete() )
		{
			return false ;
		}
		
		// 移动下级分类
		if( !$aDB->Update( $sHubTable
				, array($sKeyBorderLft=>JCAT_DB::e("`{$sKeyBorderLft}`-1"), $sKeyBorderRgt=>JCAT_DB::e("`{$sKeyBorderRgt}`-1"))
				, "`{$sKeyBorderLft}` BETWEEN {$nLft} AND {$nRgt}" ) )
		{
			return false ;
		}
		
		// 移动后面的分类
		if( !$aDB->Update( $sHubTable
				, array($sKeyBorderLft=>JCAT_DB::e("`{$sKeyBorderLft}`-2"), $sKeyBorderRgt=>JCAT_DB::e("`{$sKeyBorderRgt}`-2"))
				, "`{$sKeyBorderLft}`>{$nRgt}" ) )
		{
			return false ;
		}

		return true ;
	}
	
	/**
	 * if $nParentCategoryId==null, this model will be top category
	 *
	 * @access	public
	 * @return	void
	 */
	public function Create()
	{		
		// 做为顶层分类插入
		if( $this->GetLeftBorder()<1 and $this->GetRightBorder()<1 )
		{
			$aORM = $this->GetORM() ;
			$aDB = $aORM->GetDB() ;
		
			// get the max right border
			$sKeyBorderRgt = $aORM->GetFieldByProperty(self::BORDER_RIGHT) ;
			$sHubTable = $aORM->GetHubTable() ;
			
			// $aDB->Query("SELECT max(`{$sKeyBorderRgt}`) FROM {$sHubTable}") ;
			$nMaxRgt = $aDB->SelectOneField($sHubTable,"max(`{$sKeyBorderRgt}`)") ;
			$nLft = $nMaxRgt? intval($nMaxRgt)+1: 1 ;
			
			// set left border and right border of this category
			$this->SetBorder($nLft,true) ;
		}
		
		// 递归写入下级分类
		$aIter = $this->CreateModelIterator() ;
		for ( $aIter->First(); !$aIter->IsDone(); $aIter->Next() )
		{
			$aChild = $aIter->Current() ;
			if( !$aChild->Create() )
			{
				return false ;
			}
		}
		
		// 写入操作
		return parent::Create() ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @return	int
	 */
	public function GetAdjacWidth()
	{
		return 2 + $this->GetChildrenCount(true) ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @return	int
	 */
	public function GetLeftBorder()
	{
		return $this->Get(self::BORDER_LEFT) ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @return	int
	 */
	public function GetRightBorder()
	{
		return $this->Get(self::BORDER_RIGHT) ;
	}

	/**
	 * set this category's left and right border pos,and return the category's width
	 *
	 * @access	public
	 * @param	$nLeft		int		the left border pos
	 * @return	void
	 */
	public function SetBorder($nLeft,$bRecursion=true)
	{
		parent::Set(self::BORDER_LEFT,$nLeft) ;
		
		// 设置下级分类
		if( $bRecursion )
		{
			$nPos = $nLeft ;
			$aIter = $this->CreateModelIterator() ;
			for ( $aIter->First(); !$aIter->IsDone(); $aIter->Next() )
			{
				$aChild = $aIter->Current() ;
				
				$aChild->SetBorder(++$nPos,true) ;
				$nPos = $aChild->GetRightBorder() ;
			}
			
			// 设置自己的右边界
			parent::Set(self::BORDER_RIGHT,++$nPos) ;
			
			return $nPos-$nLeft+1 ;
		}
		
		// 不改变下级分类的左右边界
		else
		{
			$nWidth = $this->GetAdjacWidth() ;
			parent::Set(self::BORDER_RIGHT,$nLeft+$this->GetAdjacWidth()-1) ;
			return $nWidth ;
		}
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @return	void
	 */
	public function Load()
	{
		$this->Loading(true) ;
		$bRet = parent::Load() ;
		$this->Loading(false) ;
		
		return $bRet ;
	}
	
	/**
	 * Description
	 *
	 * @param	$sDataName			string	What's this Parameter ?
	 * @param	$Value				mixed	What's this Parameter ?
	 * @param	$bSetChanged=true	bool	What's this Parameter ?
	 * @return	void
	 */
	public function Set( $sDataName, $Value, $bSetChanged=true )
	{
		if( !$this->bLoading and in_array($sDataName,array(self::BORDER_LEFT,self::BORDER_RIGHT)))
		{
			throw new JCAT_Exception(JCAT_Language::SentenceEx(
				'禁止改变属性：%s'
				, 'JCAT', null
				, $sDataName
			)) ;
		}
		
		parent::Set($sDataName, $Value, $bSetChanged) ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @param	$bLoading=true
	 * @return	void
	 */
	public function Loading($bLoading=true)
	{
		$this->bLoading = $bLoading ;
	}
	
	
	// 属性 ///////////////////////////////////
	
	/**
	 * Description
	 * 
	 * @access	private
	 * @var		int
	 */
	private $nTreeDepth = -1 ;
	
	/**
	 * Description
	 * 
	 * @access	private
	 * @var		bool
	 */
	private $bLoading = false ;
}


?>